package org.folio.rest.impl.other.blacklist;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.folio.rest.impl.other.PartyLogSave;
import org.folio.rest.impl.other.PartyUtil;
import org.folio.rest.impl.other.take.TakeInterface;
import org.folio.rest.impl.util.DBName;
import org.folio.rest.impl.util.ParallelAsync;
import org.folio.rest.impl.util.ParallelResult;
import org.folio.rest.jaxrs.model.*;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.persist.cql.CQLWrapper;

import java.util.*;
import java.util.stream.Collectors;

public class BlacklistService implements TakeInterface {


    private final static Logger logger = LoggerFactory.getLogger("modparty");
    private static Vertx vertx;
    private static String tenant;
    private static Boolean rulesStart = false;
    private static Rule rule;


    public BlacklistService(Vertx vertx,String tenant) {
        this.vertx = vertx;
        BlacklistService.tenant=tenant;
        initBlackRule();

    }

    public void initBlackRule() {
        PostgresClient.getInstance(vertx, tenant).get(DBName.NOTIFY_TABLE_PARTY_RULES, new Rule(), true, reply -> {
            if (reply.succeeded()) {
                List<Rule> ruleList = reply.result().getResults();
                Integer year = DateUtil.year(new Date());
                Optional<Rule> ruleOp = ruleList.stream().filter(a -> a.getYear().equals(year.toString()) && a.getStatus().equals("1") && a.getOpenState().equals("1")).findAny();
                if (ruleOp.isPresent()) {
                    this.rulesStart = true;
                    this.rule = ruleOp.get();
                    logger.info("黑名单规则已经启动....");
                }else{
                    logger.info("未找到合适的黑名单规则...");
                }
            }
        });
    }

    /**
     * 根据黑名单规则的启动或者关闭 启用黑名单规则
     *
     * @param value
     */
    public static void addBlackListRule(Rule value) {
        if (value.getOpenState().equals("1")) {
            startBlackListRule(value);
        } else {
            stopBlackListRule(value);
        }
    }

    private static void startBlackListRule(Rule value) {
        Integer year = DateUtil.year(new Date());
        if (value.getYear().equals(year.toString()) && value.getOpenState().equals("1")) {
            rulesStart = true;
            rule = value;
            logger.info("黑名单规则已经启动....");
        }
    }

    private static void stopBlackListRule(Rule value) {
        Integer year = DateUtil.year(new Date());
        if (value.getYear().equals(year.toString()) &&
                rulesStart &&
                rule != null &&
                rule.getId().equals(value.getId())) {
            rulesStart = false;
            rule = null;
            logger.info("黑名单规则已经关闭....");
        }
    }

    private static void blackListStop() {
        String query = "( status == 1 )";
        CQLWrapper cql = PartyUtil.CQLCreate(query, -1, -1, DBName.NOTIFY_TABLE_READER_CREDIT);
        PostgresClient pg = PostgresClient.getInstance(vertx, tenant);
        pg.get(DBName.NOTIFY_TABLE_READER_CREDIT, ReaderCreditRecord.class, new String[]{"*"}, cql, true, false, reply -> {
            if (reply.succeeded()) {
                List<ReaderCreditRecord> list = reply.result().getResults();
                String date = DateUtil.offsetDay(new Date(), -1).toDateStr();
               if (list.size() > 0) {
                    list.forEach(a -> {
                        Optional<PunishmentRecordGroup> blackListRecode = a.getPunishmentRecordGroup().stream().filter(
                                c -> DateUtil.parse(c.getPrisonTerm()).isBeforeOrEquals(DateUtil.parseDate(date)) && c.getStatus().equals("1")).findAny();
                        if (blackListRecode.isPresent()) {
                            a.getPunishmentRecordGroup().stream().forEach(c -> c.setStatus("0"));
                            a.getConductRecordGroup().stream().forEach(b -> b.setStatus("0"));
                            a.setConductRecordGroup(a.getConductRecordGroup());
                            a.setPunishmentRecordGroup(a.getPunishmentRecordGroup());
                            pg.update(DBName.NOTIFY_TABLE_READER_CREDIT, a, a.getId(), updateReply -> {
                                a.getMetadata().setCreatedDate(new Date());
                                a.getMetadata().setUpdatedDate(new Date());
                                logger.info("黑名单记录修改完成:"+a.getReaderInfo().getBarcode());
                                PartyLogSave.saveLog("系统自动修改用户名称为【"+ a.getReaderInfo().getName()+"】- "+blackListRecode.get().getPrisonTerm() +"- 到期的黑名单记录",3, a.getMetadata(),pg);
                                return;
                            });

                        }
                    });
                }
            }
        });
    }

    /**
     * 修改用户的行为记录 并且检查用户是否符合黑名单的标准
     * 使用范围 签到结束之后的处理流程中, 后台修改用户签到状态的接口中
     *
     * @param attend 签到记录
     */
    private  void recordBehaviorAndCheck(Attend attend) {
        if (rulesStart) {
            /*   unarrived 0 缺席
                    arrived 1 yiqiand
            lateed 2 迟到
                    leave  3 请假
            early 4 早退
                    unsigned 5 未签到*/
            Integer attendState = attend.getAttendState();
            PostgresClient pg = PostgresClient.getInstance(vertx, tenant);
            //预防 突然篡改 签到状态 而引发的记录异常
            if (!"1".equals(attendState)) {
                ReaderReserveGroup user = attend.getReaderReserveGroup().get(0);

                CQLWrapper cql = PartyUtil.CQLCreate("( readerInfo.barcode ==  *" + user.getBarcode() + "* )", 1, 0, DBName.NOTIFY_TABLE_READER_CREDIT);
                pg.get(DBName.NOTIFY_TABLE_READER_CREDIT, ReaderCreditRecordGroup.class, new String[]{"*"}, cql, true, false, queryReply -> {
                    if (queryReply.succeeded()) {
                        List<ReaderCreditRecordGroup> readerCreditRecodeList = queryReply.result().getResults();
                        if (readerCreditRecodeList.size() == 1) {
                            ReaderCreditRecordGroup readerCreditRecode = readerCreditRecodeList.get(0);
                            Optional<ConductRecordGroup> readerConductRecord = readerCreditRecode.getConductRecordGroup().stream().filter(a -> a.getRuleId().equals(rule.getId()) && a.getStatus().equals("1")).findAny();
                            if (readerConductRecord.isPresent()) {
                                //用户行为记录存在
                                ConductRecordGroup record = readerConductRecord.get();

                                pg.getById(DBName.NOTIFY_TABLE_READER_CREDIT_RELATED, attend.getId(), BlackListRecordRelated.class, getReply -> {
                                    if (getReply.succeeded()) {
                                        BlackListRecordRelated result = getReply.result();
                                        if (result == null) {
                                            //如果是空,证明从来没有添加过这个记录,可以正常添加
                                            checkBlackListAndSaveRecord(readerCreditRecode, record, attend.getAttendState().toString());
                                            updateReaderCredit(pg, readerCreditRecode, record);
                                            SaveBlackListRecordRelated(attend, pg, user, record);
                                            return;
                                        } else {
                                            //证明曾经有过这个记录, 需要进行对比 并且 复原
                                            //针对特定记录 ,避免再度陷入黑名单
                                            //用户行为记录,将会在黑名单执行完成之后,被冻结,并且赋予新对象
                                            if (result.getReaderRecordId().equals(record.getId()) && !result.getAttendStatus().equals(attend.getAttendState().toString())) {
                                                //针对旧记录复原
                                                switch (result.getAttendStatus().toString()) {
                                                    case "0":
                                                        int absence = record.getAbsence() - 1;
                                                        record.setAbsence(absence);
                                                        break;
                                                    case "2":
                                                        int beLate = record.getBeLate() - 1;
                                                        record.setBeLate(beLate);
                                                        break;
                                                    case "3":
                                                        int leave = record.getLeave() - 1;
                                                        record.setLeave(leave);
                                                        break;
                                                    case "4":
                                                        int leaveEarly = record.getLeaveEarly() - 1;
                                                        record.setLeaveEarly(leaveEarly);
                                                        break;
                                                }
                                                //检查黑名单 触发情况
                                                checkBlackListAndSaveRecord(readerCreditRecode, record, attend.getAttendState().toString());
                                                updateReaderCredit(pg, readerCreditRecode, record);
                                                //回写记录
                                                result.setAttendStatus(attend.getAttendState().toString());
                                                pg.update(DBName.NOTIFY_TABLE_READER_CREDIT_RELATED, result, result.getId(), update -> {
                                                    return;
                                                });
                                                return;
                                            }
                                            //如果不相等,就不做处理,避免再度陷入黑名单处罚
                                        }
                                    }
                                });

                            } else {
                                //用户行为记录全被冻结需要 新建用户行为记录 新建黑名单记录 新建关联记录
                                //应该避免旧记录污染
                                pg.getById(DBName.NOTIFY_TABLE_READER_CREDIT_RELATED, attend.getId(), BlackListRecordRelated.class, getReply -> {
                                    if (getReply.succeeded()) {
                                        BlackListRecordRelated result = getReply.result();
                                        //如果是空,证明从来没有添加过这个记录,可以正常添加
                                        if (result == null) {
                                            ConductRecordGroup record = buildConductRecord();
                                            checkBlackListAndSaveRecord(readerCreditRecode, record, attend.getAttendState().toString());
                                            updateReaderCredit(pg, readerCreditRecode, record);
                                            SaveBlackListRecordRelated(attend, pg, user, record);
                                            return;
                                        }
                                        return;
                                    }
                                });

                            }
                        } else {
                            //完全重建用户行为记录

                            ReaderCreditRecordGroup readerCreditRecordGroup = new ReaderCreditRecordGroup();
                            ReaderInfo readerInfo = new ReaderInfo();
                            ReaderReserveGroup readerInfoGroup = attend.getReaderReserveGroup().get(0);
                            readerInfo.setBarcode(readerInfoGroup.getBarcode());
                            readerInfo.setEmail(readerInfoGroup.getEmail());
                            readerInfo.setMobilePhone(readerInfoGroup.getMobilePhone());
                            readerInfo.setName(readerInfoGroup.getName());

                            readerCreditRecordGroup.setId(UUID.randomUUID().toString());
                            readerCreditRecordGroup.setCreateDate(DateUtil.formatDate(new Date()));
                            readerCreditRecordGroup.setStatus("1");

                            ConductRecordGroup conductRecordGroup = buildConductRecord();
                            readerCreditRecordGroup.setReaderInfo(readerInfo);
                            readerCreditRecordGroup.setMetadata(attend.getMetadata());

                            checkBlackListAndSaveRecord(readerCreditRecordGroup, conductRecordGroup, attend.getAttendState().toString());
                            List<ConductRecordGroup> conductRecordGroups = new ArrayList<>();
                            conductRecordGroups.add(conductRecordGroup);
                            readerCreditRecordGroup.setConductRecordGroup(conductRecordGroups);
                            pg.save(DBName.NOTIFY_TABLE_READER_CREDIT, readerCreditRecordGroup.getId(), readerCreditRecordGroup, reply -> {
                                return;
                            });
                            SaveBlackListRecordRelated(attend, pg, user, conductRecordGroup);
                            return;
                        }
                    }
                });
            }

        }
        //黑名单关闭
    }


    private static ConductRecordGroup buildConductRecord() {
        ConductRecordGroup record = new ConductRecordGroup();
        record.setLeaveEarly(0);
        record.setLeave(0);
        record.setBeLate(0);
        record.setAbsence(0);
        record.setCreateDate(DateUtil.date().toDateStr());
        record.setId(UUID.randomUUID().toString());
        record.setRuleId(rule.getId());
        record.setStatus("1");
        return record;
    }

    private static void SaveBlackListRecordRelated(Attend attend, PostgresClient pg, ReaderReserveGroup user, ConductRecordGroup record) {
        BlackListRecordRelated blackListRecordRelated = new BlackListRecordRelated();
        blackListRecordRelated.setAttendId(attend.getId());
        blackListRecordRelated.setAttendStatus(attend.getAttendState().toString());
        blackListRecordRelated.setBarcode(user.getBarcode());
        blackListRecordRelated.setCreateDate(DateUtil.date().toDateStr());
        blackListRecordRelated.setId(UUID.randomUUID().toString());
        blackListRecordRelated.setReaderRecordId(record.getId());
        //保存积分记录
        pg.save(DBName.NOTIFY_TABLE_READER_CREDIT_RELATED, blackListRecordRelated.getId(), blackListRecordRelated, saveReply -> {
            return;
        });
    }

    private static void updateReaderCredit(PostgresClient pg, ReaderCreditRecordGroup readerCreditRecode, ConductRecordGroup record) {
        List<ConductRecordGroup> conductRecord = readerCreditRecode.getConductRecordGroup().stream().filter(a -> !a.getId().equals(record.getId())).collect(Collectors.toList());
        conductRecord.add(record);
        readerCreditRecode.setConductRecordGroup(conductRecord);
        //更新行为记录
        pg.update(DBName.NOTIFY_TABLE_READER_CREDIT, readerCreditRecode, readerCreditRecode.getId(), updateReply -> {
            return;
        });
    }

    private static void checkBlackListAndSaveRecord(ReaderCreditRecordGroup readerCreditRecode, ConductRecordGroup record, String state) {
        switch (state) {
            case "0":
                int absence = record.getAbsence() + 1;
                record.setAbsence(absence);
                break;
            case "2":
                int beLate = record.getBeLate() + 1;
                record.setBeLate(beLate);
                break;
            case "3":
                int leave = record.getLeave() + 1;
                record.setLeave(leave);
                break;
            case "4":
                int leaveEarly = record.getLeaveEarly() + 1;
                record.setLeaveEarly(leaveEarly);
                break;
        }
        Boolean flag = false;
        PunishmentRecordGroup punishmentRecordGroup = new PunishmentRecordGroup();
        DateTime prisonTerm = DateUtil.offsetDay(new Date(), rule.getPrisonTermDay());
            //校验黑名单规则
        if (record.getAbsence() >= rule.getAbsence()) {
            //触发黑名单
            flag = true;
            //您因未到X次（原因）已被拉入黑名单，在XXXX年xx月xx日内不允许报名
            punishmentRecordGroup.setFoulDescription("您因【未到次数:" + record.getAbsence() + " 】次，已被拉入黑名单，" +
                    "在 【" + DateUtil.formatChineseDate(prisonTerm, false) + "】内不允许报名");
        }
        if (record.getLeave() >= rule.getLate() && !flag) {
            flag = true;
            punishmentRecordGroup.setFoulDescription("您因【请假次数:" + record.getLeave() + " 】次，已被拉入黑名单，" +
                    "在 【" + DateUtil.formatChineseDate(prisonTerm, false) + "】内不允许报名");
        }
        if (record.getBeLate() >= rule.getLate() && !flag) {
            flag = true;
            punishmentRecordGroup.setFoulDescription("您因【迟到次数:" + record.getBeLate() + " 】次，已被拉入黑名单，" +
                    "在 【" + DateUtil.formatChineseDate(prisonTerm, false) + "】内不允许报名");
        }
        if (record.getLeaveEarly() >= rule.getLeaveEarly() && !flag) {
            flag = true;
            punishmentRecordGroup.setFoulDescription("您因【早退次数:" + record.getLeaveEarly() + " 】次，已被拉入黑名单，" +
                    "在 【" + DateUtil.formatChineseDate(prisonTerm, false) + "】内不允许报名");
        }
        if (flag) {
            punishmentRecordGroup.setPrisonTermDay(rule.getPrisonTermDay());
            punishmentRecordGroup.setPrisonTerm(prisonTerm.toDateStr());
            punishmentRecordGroup.setCreateDate(DateUtil.date().toDateStr());
            punishmentRecordGroup.setId(UUID.randomUUID().toString());
            punishmentRecordGroup.setRuleId(rule.getId());
            punishmentRecordGroup.setConductId(record.getId());
            punishmentRecordGroup.setStatus("1");
            List<PunishmentRecordGroup> list = readerCreditRecode.getPunishmentRecordGroup();
            list.forEach(a -> a.setStatus("0"));
            list.add(punishmentRecordGroup);
            readerCreditRecode.setPunishmentRecordGroup(list);
            //假如这个人已经在黑名单
        } else {
            List<PunishmentRecordGroup> list = readerCreditRecode.getPunishmentRecordGroup();
            list.forEach(a -> a.setStatus("0"));
            readerCreditRecode.setPunishmentRecordGroup(list);
        }
    }

    @Override
    public void setJob(Attend attend) {
        recordBehaviorAndCheck(attend);
    }

    @Override
    public void timeTake() {
        blackListStop();
    }

    @Override
    public void switchRules() {

        PostgresClient.getInstance(vertx, tenant).get(DBName.NOTIFY_TABLE_PARTY_RULES, new Rule(), true, reply -> {
            if (reply.succeeded()) {
                List<Rule> ruleList = reply.result().getResults();
                String year =""+ DateUtil.offsetDay(new Date(),-1).dayOfYear();
                Optional<Rule> ruleOp = ruleList.stream().filter(a -> a.getYear().equals(year) && a.getStatus().equals("1") && a.getOpenState().equals("1")).findAny();
                if (ruleOp.isPresent()) {

                    Rule rules = ruleOp.get();
                    rules.setStatus("0");
                    rules.setOpenState("0");
                    String newYear = ""+ DateUtil.year(new Date());
                    Optional<Rule> newRules = ruleList.stream().filter(a -> a.getYear().equals(newYear) && a.getStatus().equals("1") && a.getOpenState().equals("1")).findAny();
                    if (this.rule.getYear().equals(year) ){
                        this.rule = null;
                        logger.info(year+"年度的黑名单规则已经结束....");
                    }
                    if (newRules.isPresent()){
                        this.rule = newRules.get();
                    }
                    PostgresClient.getInstance(vertx,tenant).update(DBName.NOTIFY_TABLE_PARTY_RULES,rule,rule.getId(),updateReply->{
                        if (updateReply.succeeded()){
                            PostgresClient.getInstance(vertx,tenant).get(DBName.NOTIFY_TABLE_READER_CREDIT,new ReaderCreditRecord(),true,getReply->{
                                if (getReply.succeeded()){
                                    List<ReaderCreditRecord> readerRecord = getReply.result().getResults();
                                    ParallelAsync parallelAsync = new ParallelAsync();
                                    readerRecord.forEach(a->{
                                        a.getConductRecordGroup().stream().filter(b->b.getRuleId().equals(rules.getId())).forEach(c->{
                                            c.setStatus("0");
                                        });
                                        parallelAsync.addAsyncTask(batchUpdate(a));
                                    });
                                    parallelAsync.start();
                                }
                            });
                        }
                    });



                }else{
                    logger.info("未找到合适的黑名单规则...");
                }
            }
        });
    }

    private Future<ParallelResult> batchUpdate(ReaderCreditRecord a){
        Future<ParallelResult> future = Future.future();
        ParallelResult parallelResult = new ParallelResult();
        PostgresClient.getInstance(vertx,tenant).update(DBName.NOTIFY_TABLE_READER_CREDIT,a,a.getId(),updateReply->{

            parallelResult.setFlag(false);
            if (updateReply.succeeded()){
                parallelResult.setFlag(true);
             }
            future.complete(parallelResult);
        });
        return future;
    }
}
